import moment from 'moment';
import mqtt from 'mqtt';
import config  from './configuration.js';
import db  from './mysqlHelper.js';
import logger from './logger.js';


const mqttConfig = config.mqttConfig;
const publish_topic = `/v2/EM/862361075045701/tx`
const Timeout_Value = 12; //超时时间 5秒

//定义一个map 用于存储指令的应答状态
let ackMap = new Map();


const client = mqtt.connect(`mqtt://${mqttConfig.host}:${mqttConfig.port}`, { username: mqttConfig.username, password: mqttConfig.password });
client.on('connect', async () => {
    console.log('mqtt连接成功');
    let topic = '/v2/EM/+/rx';
    client.subscribe(topic, async (err) => {
        if (!err) {
            console.log(`成功订阅主题: ${topic}`);
        } else {
            console.error(`订阅主题 ${topic} 失败:`, err);
        }
    });
});

client.on('message', async (topic, message) => {
    console.log(`收到消息:${topic}  ${message.toString()} `);
    parsePacket(message.toString());
})




// CRC8 查找表
const CRC8_Table = [
    0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15, 0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
    0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65, 0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
    0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5, 0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
    0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85, 0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
    0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2, 0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
    0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2, 0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
    0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32, 0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
    0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42, 0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
    0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C, 0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
    0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC, 0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
    0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C, 0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
    0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C, 0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
    0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B, 0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
    0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B, 0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
    0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB, 0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
    0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB, 0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3
];

/**
 * 计算 CRC8 校验值
 * @param {Buffer} buffer - 输入数据的 Buffer
 * @returns {number} - CRC8 校验值
 */
function calculateCRC8(buffer, length) {
    let crc = 0;

    for (let i = 0; i < length; i++) {
        crc ^= buffer[i];         // 先异或
        crc = CRC8_Table[crc];    // 再查表
    }

    return crc;
}


async function sleep(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
}

//解释接收到的数据包
function parsePacket(data) {
    // try {
    let buf = Buffer.from(data, 'hex');
    let object = {};
    let index = 0;

    object.MAC = buf.subarray(0, 6).toString('hex').toUpperCase(); index += 6;
    object.ID = buf.subarray(index, index + 1).toString('ascii'); index += 1;
    // let crc_result = calculateCRC8(buf,buf.length - 1);
    // if(crc_result !== buf[buf.length - 1]) {
    //     console.error('CRC校验失败:', crc_result, buf[buf.length - 1]);
    //     return;
    // }
    let key = `${object.MAC}${object.ID}`
    switch (object.ID) {
        case 'T':
            object.TICK = buf.readUInt16BE(index); index += 2;
            object.ICCID = buf.subarray(index, index + 20).toString('ascii').trim(); index += 20;
            object.RSSI = buf.readUInt8(index); index += 1;
            object.EMT = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EME = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMPWR = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMV = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMC = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMSW = buf.readUInt8(index); index += 1;
            object.EMSTAT = buf.readUInt8(index); index += 1;
            console.log(moment().format('YYYY-MM-DD HH:mm:ss'), '心跳数据:', JSON.stringify(object));


            //创建应答包
            let param = Buffer.allocUnsafe(3)
            param.writeUInt8(0x00, 0); // 0x00表示应答
            param.writeUint16BE(object.TICK, 1); // 2字节的TICK
            let packet = createPacket(object.MAC, 'T', param);
            publish(publish_topic, packet);
            delete object.TICK;
            delete object.ID;
            refreshMeterInfo(object);
            break;
        case 'Q':
            object.SAP = buf.readUInt8(index); index += 1;
            object.ACKU = buf.readUInt8(index); index += 1;
            object.EMT = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EME = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMPWR = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMV = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMC = buf.readUInt32BE(index) * 0.001; index += 4;
            object.EMSW = buf.readUInt8(index); index += 1;
            object.EMSTAT = buf.readUInt8(index); index += 1;
            ackMap.set(key, object);
            console.log('状态查询应答数据:', JSON.stringify(object));

            refreshMeterInfo(object,);
            break;
        case 'E':
            object.SAP = buf.readUInt8(index); index += 1;
            object.ACKU = buf.readUInt8(index); index += 1;
            object.VALUE = buf.readUInt32BE(index) * 0.001; index += 4;
            ackMap.set(key, object.ACKU);
            console.log(`${object.MAC}设置电量应答数据:`, JSON.stringify(object));
            break;
        case 'W':
            object.SAP = buf.readUInt8(index); index += 1;
            object.EMSW = buf.readUInt8(index); index += 1;
            ackMap.set(key, object.EMSW);
            console.log(`${object.MAC}开关控制应答数据:`, JSON.stringify(object));
            break;
        case 'R':
            object.SAP = buf.readUInt8(index); index += 1;
            object.ACKU = buf.readUInt8(index); index += 1;
            ackMap.set(key, object.ACKU);
            console.log(`${object.MAC}远程重启应答:`, JSON.stringify(object));
            break;
        case 'U':
            object.SAP = buf.readUInt8(index); index += 1;

            object.OPTION = buf.readUInt8(index); index += 1;
            object.OPTION = object.OPTION & 0x3F; // 取低7位
            object.ACKU = buf.readUInt8(index); index += 1;
            if (object.OPTION == 1 || object.OPTION == 4 || object.OPTION == 62) {
                object.VALUE = buf.readUInt32BE(index); index += 4;
            } else {
                object.VALUE = buf.readUInt32BE(index) * 0.001; index += 4; // 
            }
            ackMap.set(key, { ACKU: object.ACKU, OPTION: object.OPTION, VALUE: object.VALUE });
            console.log(`${object.MAC}参数查询或写入应答数据:`, JSON.stringify(object));
            break;
        case 'C':
            object.ACKU = buf.readUInt8(index); index += 1;
            ackMap.set(key, object.ACKU);
            console.log(`${object.MAC}设置服务中心应答数据:`, JSON.stringify(object));
            break;
        default:
            console.error(`${object.MAC}未知数据包类型:`, object.ID);
            return;
    }



    // } catch (error) {
    //     console.error('解析数据包失败:', error.message);
    // }
}

function createPacket(MAC, ID, data) {
    let buf = Buffer.alloc(0);
    let crc = 0;
    let packet = Buffer.from(MAC, 'hex');
    packet = Buffer.concat([packet, Buffer.from(ID, 'ascii'), data]);
    console.log('数据包:', packet.toString('hex').toUpperCase());
    crc = calculateCRC8(packet, packet.length);
    packet = Buffer.concat([packet, Buffer.from([crc])]);
    return packet.toString('hex').toUpperCase();
}



async function publish(topic, message) {
    return new Promise((resolve) => {
        try {
            client.publish(topic, message);
            console.info('发布成功', topic, message);
            resolve({ code: 1, msg: 'success' });
        } catch (error) {
            console.error('发布失败', topic, message, error.message);
            resolve({ code: 0, msg: '发布失败:' + error.message });
        }
    })
}
/**
 * 设取电表当前的状态
 */
export async function getEMT(mac) {
    let param = Buffer.allocUnsafe(2)
    param.writeUInt8(2, 0);
    let packet = createPacket(mac, 'Q', param);
    console.log(`${mac}发送数据包:`, packet);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's').unix();
    let key = `${mac}Q`;
    while (moment().unix() < timeout && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack.ACKU == 0) {
            console.log(`${mac}查询状态成功:`, ack);
            return { code: 1, msg: '查询状态成功', data: ack };
        } else {
            console.error(`${mac}查询状态失败:`, ack.ACKU);
            return { code: 0, msg: '查询状态失败', data: ack };
        }
    } else {
        console.error(`${mac}查询状态超时:`, key);
        return { code: 0, msg: '查询状态超时', data: null };
    }
}

/**
 * 设置可用电量 建议设置前先查询电量 累加充值电量后再下发
 * @param {*} mac 表的MAC地址
 * @param {*} value 电表当前可用电量，单位为度
 */
export async function setEME(mac, value) {
    let param = Buffer.allocUnsafe(6)
    param.writeUInt8(2, 0);
    param.writeUInt32BE(value * 1000, 1); // 4字节的EME
    let packet = createPacket(mac, 'E', param);
    console.log(`${mac}发送数据包:`, packet);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's');
    let key = `${mac}E`;
    while (moment().isBefore(timeout) && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack == 0) {
            console.log(`${mac}设置电量成功:`, ack);
            return { code: 1, msg: '设置电量成功' };
        } else {
            console.error(`${mac}设置电量失败:`, ack);
            return { code: 0, msg: '设置电量失败' };
        }
    } else {
        console.error(`${mac}设置电量超时:`, key);
        return { code: 0, msg: '设置电量超时' };
    }
}
/**
 * 拉合闸控制
 * @param {*} mac 表的MAC地址
 * @param {*} status 1:合闸 2:拉闸
 */
async function switchControl(mac, status) {
    let param = Buffer.allocUnsafe(2);
    param[0] = 2;
    param[1] = status;
    let packet = createPacket(mac, 'W', param);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's').unix();
    let key = `${mac}W`;
    while (moment().unix() < timeout && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack == status) {
            console.log(`${mac}拉合闸控制成功:`, ack);
            return { code: 1, msg: '开关控制成功' };
        } else {
            console.error(`${mac}开关控制失败:`, ack);
            return { code: 0, msg: '开关控制失败' };
        }
    } else {
        console.error(`${mac}开关控制超时:`, key);
        return { code: 0, msg: '开关控制超时' };
    }
}
/**
 * 读取电表参数
 * @param {*} mac 表的MAC地址
 * @param {*} param_no 参数编号 1计量模式 出厂默认1  2 计量系数  出厂默认1  3 表当前的累计用电量 4 心跳间隔 出厂默认45秒 5 拉闸门限 出厂默认0.1 6 告警电流 出厂默认 0.08 7 低电量告警门限 默认10  
 * @returns 
 */
async function queryParam(mac, param_no) {
    let param = Buffer.alloc(6).fill(0);
    param[0] = 2;
    param[1] = param_no;
    let packet = createPacket(mac, 'U', param);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's').unix();
    let key = `${mac}U`;
    while (moment().unix() < timeout && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack.ACKU == 0) {
            console.log('查询参数成功:', ack.VALUE);
            return { code: 1, msg: '查询参数成功', data: ack.VALUE };
        } else {
            console.error('查询参数失败:', ack.OPTION);
            return { code: 0, msg: '查询参数失败', data: ack.VALUE };
        }
    } else {
        console.error('查询参数超时:', key);
        return { code: 0, msg: '查询参数超时', data: null };
    }

}
/**
 * 设置电表的参数   
 * @param {*} mac 表的MAC地址
 * @param {*} param_no 参数编号 1计量模式 出厂默认1  2 计量系统  出厂默认1  3 最表当前的累计用电量 4 心跳间隔 出厂默认45秒 5 拉闸门限 出厂默认0.1 6 告警电流 出厂默认 0.08 7 低电量告警门限 默认10
 * @param {*} value 参数值
 * @returns 
 */
async function writeParam(mac, param_no, value) {
    let param = Buffer.alloc(6).fill(0);
    param[0] = 2;
    if (param_no == 1 || param_no == 4 || param_no == 62) {
        param.writeUInt32BE(value, 2); // 4字节的EME
    } else {
        param.writeUInt32BE(value * 1000, 2); // 4字节的EME
    }
    param[1] = (param_no | 0x80);
    console.log('写入参数:', param[1]);
    let packet = createPacket(mac, 'U', param);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's').unix();
    let key = `${mac}U`;
    while (moment().unix() < timeout && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack.OPTION == param_no) {
            console.log('写入参数成功:', ack.VALUE);
            return { code: 1, msg: '写入参数成功', data: ack.VALUE };
        } else {
            console.error('写入参数失败:', ack.OPTION);
            return { code: 0, msg: '写入参数失败', data: ack.VALUE };
        }
    } else {
        console.error('写入参数超时:', key);
        return { code: 0, msg: '写入参数超时', data: null };
    }

}

/**
 * 设置服务中心
 * 
 */
async function setServer(mac, ip, port, name, password, path) {
    let param = Buffer.alloc(90).fill(0);
    let mac_buf = Buffer.from(mac, 'hex');
    let ip_buf = Buffer.from(ip, 'ascii')
    let port_buf = Buffer.alloc(2);
    port_buf.writeUInt16BE(port, 0);
    let name_buf = Buffer.from(name, 'ascii')
    let password_buf = Buffer.from(password, 'ascii')
    let path_buf = Buffer.from(path, 'ascii')
    let index = 0;
    ip_buf.copy(param, index, 0, ip_buf.length); index += 32; // IP地址
    port_buf.copy(param, index, 0, port_buf.length); index += 2; // 端口号
    name_buf.copy(param, index, 0, name_buf.length); index += 16; // 用户名
    password_buf.copy(param, index, 0, password_buf.length); index += 16; // 密码
    path_buf.copy(param, index, 0, path_buf.length); index += 32; // 路径
    let packet = createPacket(mac, 'C', param);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's').unix();
    let key = `${mac}C`;
    while (moment().unix() < timeout && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack == 0) {
            console.log('设置服务中心成功:', ack);
            return { code: 1, msg: '设置服务中心成功', data: ack };
        } else {
            console.error('设置服务中心失败:', ack);
            return { code: 0, msg: '设置服务中心失败', data: ack };
        }
    } else {
        console.error('设置服务中心超时:', key);
        return { code: 0, msg: '设置服务中心超时', data: null };
    }

}

async function reboot(mac) {
    let param = Buffer.alloc(1);
    param[0] = 2;
    let packet = createPacket(mac, 'R', param);
    await publish(publish_topic, packet);
    let timeout = moment().add(Timeout_Value, 's').unix();
    let key = `${mac}R`;
    while (moment().unix() < timeout && !ackMap.has(key)) {
        await sleep(100);
    }
    if (ackMap.has(key)) {
        let ack = ackMap.get(key);
        ackMap.delete(key);
        if (ack == 0) {
            console.log('远程重启成功:', ack);
            return { code: 1, msg: '远程重启成功', data: ack };
        } else {
            console.error('远程重启失败:', ack);
            return { code: 0, msg: '远程重启失败', data: ack };
        }
    } else {
        console.error('远程重启超时:', key);
        return { code: 0, msg: '远程重启超时', data: null };
    }


}


/**
 * 刷新电表信息
 */
async function refreshMeterInfo(realData) {

    let mac = realData.MAC;
    let sql = `select * from meter where mac=?`;
    let meter = await db.query(sql, [mac]);
    if (meter instanceof Error) {
        logger.error(`查询电表信息出错 error :` + meter.message, `mac:${mac}`);
        return;
    }
    if (meter.length == 0) {
        logger.error(`电表不存在`, `mac:${mac}`);
        return;
    }
    //循环对比电表信息和数据库信息是否一致 不一致加入到要更新的对象中
    let updateObj = {};
    //遍历meter对象的属性
    for (const key in realData) {
        if (key == 'TICK' || key == 'ID' || key == 'SAP' || key == 'ACKU') {
            continue;
        }
        //对比realData对象的属性和meter对象的属性是否一致
        if (realData[key] != null && realData[key] != meter[0][key.toLowerCase()]) {
            updateObj[key.toLowerCase()] = realData[key];
            if (key == 'emt')//
            {
                updateObj['last_change_time'] = moment().format('YYYY-MM-DD HH:mm:ss');
            }
        }
    }
    if (meter[0].is_online == 0 || meter[0].is_online == null) {
        updateObj['is_online'] = 1;
        await try_write_eme(mac);
    }
    updateObj['last_communication_time'] = moment().format('YYYY-MM-DD HH:mm:ss');
    let res = await db.update('meter', updateObj, { mac: mac });
    if (res instanceof Error) {
        logger.error(`更新电表信息出错 error :` + res.message, `mac:${mac}`);
        return;
    }
}



async function try_write_eme(mac) {
    let sql = `select * from write_EME_try where mac='${mac}' order by create_time asc limit 1`;
    let write_EME_trys = await db.query(sql);
    if (write_EME_trys instanceof Error) {
        logger.error(`查询待写入电量出错 error :` + write_EME_trys.message, `mac:${mac}`);
        return
    }
    if (write_EME_trys.length > 0) {

        let real_meter_info = await getEMT(mac);
        if (real_meter_info.code != 1) {
            console.error('读取电表余额失败', JSON.stringify(real_meter_info));
            return;
        }
        let write_num = real_meter_info.data.EME + parseFloat(write_EME_trys[0].new_EME);//写入电表的电量
        let set_res = await setEME(mac, write_num);
        if (set_res.code != 1) {
            logger.error('写入新剩余电量失败', JSON.stringify(set_res));
            return;
        }
        //删除待写入的电量
        let sqls = [];
        //更新充值记录里的原电量和新电量
        sqls.push({ sql: `update recharge_rec set old_num=${real_meter_info.data.EME}, new_num=${write_num} where pay_sn='${write_EME_trys[0].pay_sn}'` });
        sqls.push({ sql: `delete from write_EME_try where mac=${mac} and pay_sn='${write_EME_trys[0].pay_sn}'` });
        let res = await db.transaction(sqls);
        if (res instanceof Error) {
            logger.error(`删除待写入电量出错 error :` + res.message, `mac:${mac}`);
            return { code: 0, msg: '删除待写入电量出错' };
        }
    }
}


export default {
    getEMT,
    setEME,
    queryParam,
    writeParam,
    setServer,
    reboot,
    switchControl,
};